#pragma once
#include "BoardLogicBase.h"
#include "BoardFactory.h"
#include "BoardLogicIOBase.h"
#include "Receiver.h"

namespace catan
{
	class BoardLogicClient : public IBoardLogicClient , public IBoardLogicServer
	{
	public:
		class ServerSender : public IBoardLogicServer, public IStringSender
		{
		public:
			typedef boost::archive::binary_iarchive  InSerializer;
			ServerSender(BoardLogicClient* c)
			{
				mClient = c;
			}
			void SetSender(IStringSender* val) { mSender = val; }

			template <class Protocol>
			void OnSEvent(Protocol& protocol)
			{
				std::ostringstream oss;
				boost::archive::binary_oarchive os(oss);
				int header =  Protocol::TypeValue;
				os & header;
				os & protocol;
				//type.Serialize(os);
				Send(oss.str());
			}

			void Send(const bytestring& str)
			{
				mSender->OnReceive(str);
			}

			~ServerSender()
			{
			}

			virtual void OnReceive(const bytestring& str)
			{
				std::istringstream iss(str);

				InSerializer is(iss);
				mReceiver.OnReceive(is);
			}
			template <class Protocol, class ReceiverType, class Serializer>
			class FunctionBaseTmpl : public Receiver<Serializer>::IFunctionBase<Serializer>
			{
			public:
				FunctionBaseTmpl(ReceiverType* r) : mReceiver(r){}
				virtual void OnReceive(Serializer& ss)
				{
					Protocol p;
					ss >> p;
					mReceiver->OnCEvent(p);
				}
				ReceiverType* mReceiver;
			};

			template <class Protocol>
			ServerSender& Reg()
			{
				mReceiver.Reg<Protocol>(new FunctionBaseTmpl<Protocol, BoardLogicClient, InSerializer>(mClient));
				return *this;
			}

			Receiver<InSerializer> mReceiver;

			BoardLogicClient* mClient;
			IStringSender* mSender;
			

		};

		template <class Protocol>
		void ServerEvent(Protocol& protocol)
		{
			static_cast<ServerSender*>(mServer)->OnSEvent(protocol);
		}

		template <class Protocol>
		void ClientEvent(Protocol& protocol)
		{
			mIO->OnCEvent(protocol);
		}

		
		BoardLogicClient(void)
		{
			//RegisterEvents();
		}
		
		void RegisterEvents()
		{
			ServerSender& s = * static_cast<ServerSender*>(mServer);
			s
				.Reg<SetGameStart>()
				.Reg<BuildHouseApply>()
				.Reg<BuildSettlementApply>()
				.Reg<BuildRoadApply>()
				.Reg<BuyCardApply>()
				.Reg<EndTurnApply>()
				.Reg<DieApply>()
				.Reg<SetPlayerTrun>()
				.Reg<PlayersHarvestApply>();
		}


		template <class ProtocolType>
		bool CheckResult(ProtocolType& protocol)
		{
			if (protocol.mResult != Result::Succeed)
			{
				LogError(ToWString(typeid(ProtocolType).name()) + Result::ToString(protocol.mResult));
				return false;
			}
			return true;
		}
		//CleintEvent;

		virtual void OnCEvent(SetGameStart& set)
		{
			mBoard = set.mBoard;
			ClientEvent(set);
		}

		virtual void OnCEvent(BuildHouseApply& apply)
		{
			if (!CheckResult(apply)) return ;
			VALID(CanBuildHouse(apply.mColor, apply.mId));
			BuildHouse(apply.mColor, apply.mId);
			ClientEvent(apply);
		}

		virtual void OnCEvent(BuildSettlementApply& apply)
		{
			if (!CheckResult(apply)) return ;
			VALID(CanBuildSettlement(apply.mColor, apply.mId));
			BuildSettlement(apply.mColor, apply.mId);
			ClientEvent(apply);
		}

		virtual void OnCEvent(BuildRoadApply& apply)
		{
			if (!CheckResult(apply)) return ;
			VALID(CanBuildRoad(apply.mColor, apply.mId));
			BuildRoad(apply.mColor, apply.mId);
			ClientEvent(apply);	
		}

		virtual void OnCEvent(BuyCardApply& apply){VALID(0);}
		// 		{
		// 			if (CheckResult(apply)) return ;
		// 			VALID((apply.mColor, apply.mId));
		// 			BuildRoad(apply.mColor, apply.mId);
		// 			GetBoardLogicClient()->OnCEvent(apply);	
		// 		}

		virtual void OnCEvent(EndTurnApply& apply)
		{
			if (!CheckResult(apply)) return ;
			NextColor();
			ClientEvent(apply);
		}

		virtual void OnCEvent(DieApply& apply)
		{
			mBoard->mDieNumbers.push_back(apply.mDieNumber);
			ClientEvent(apply);
		}

		virtual void OnCEvent(SetPlayerTrun& set)
		{
			if (!CheckStrategyColor(set.mColor))
			{
				return ;
			}
			if (IsPreBuilding())
			{
				SetBuildHouse set;
				ClientEvent(set);
			}
			else
			{
				ClientEvent(set);
			}

		}

		virtual void OnCEvent(PlayersHarvestApply& apply)
		{
			SetPlayerHarvestResourceCards(*apply.playersHarvestResourceCards);
			ClientEvent(apply);
		}

		virtual void OnSEvent(ProtocolBase& protocol)
		{
			switch(protocol.Type())
			{
				CASE_OnSEvent(BuildHouseReq);
				CASE_OnSEvent(BuildSettlementReq);
				CASE_OnSEvent(BuildRoadReq);
				CASE_OnSEvent(DieReq);

			default:
				VALID(0);
			}
		}

		virtual void OnSEvent(BuildHouseReq& req)
		{
			VALID(CheckStrategyColor(req.mColor) && CanBuildHouse(req.mColor, req.mId));
			//BuildHouse(req.mColor, req.mId);

			ServerEvent(req);
			if (IsPreBuilding())
			{
				SetBuildRoad set;
				ClientEvent(set);
			}
		};

		virtual void OnSEvent(BuildSettlementReq& req)
		{
			VALID(CheckStrategyColor(req.mColor) && CanBuildSettlement(req.mColor, req.mId));
			//BuildSettlement(req.mColor, req.mId);

			ServerEvent(req);
		}

		virtual void OnSEvent(BuildRoadReq& req)
		{
			VALID(CheckStrategyColor(req.mColor) && CanBuildRoad(req.mColor, req.mId));
			//BuildRoad(req.mColor, req.mId);

			ServerEvent(req);

			if (IsPreBuilding())
			{
				EndTurnReq end;
				end.mColor = req.mColor;
				ServerEvent(end);
			}
		}

		virtual void OnSEvent(DieReq& req)
		{
			VALID(CheckStrategyColor(req.mColor) && CanDie());
			ServerEvent(req);
		}

		void SetIO(IBoardLogicIO* val) { mIO = val; }

		//ServerEvent

		IBoardLogicIO* mIO;
	};

}	